package net.cassite.daf4j.resource.file;

import net.cassite.daf4j.resource.InputStreamProvider;
import net.cassite.daf4j.resource.Source;

import java.io.*;
import java.util.*;

/**
 * source of local file
 */
public class LocalFileSource implements Source {
        private File getValidatedFile(String location, boolean isDir) throws IOException {
                File file = new File(location);
                if (file.exists()) {
                        return file;
                }
                getValidatedFile(file.getParent(), true);
                if (isDir) {
                        if (!file.mkdir()) {
                                throw new IOException("Cannot make dir " + file.getAbsolutePath());
                        }
                }
                return file;
        }

        @Override
        public String separator() {
                return File.separator;
        }

        @Override
        public boolean exists(String location) throws Exception {
                return new File(location).exists();
        }

        @Override
        public boolean haveSubLocations(String location) {
                return new File(location).isDirectory();
        }

        @Override
        public List<String> subLocations(String location) throws IOException {
                File dir = getValidatedFile(location, true);
                String[] files = dir.list();
                List<String> list = new ArrayList<String>(files.length);
                Collections.addAll(list, files);
                return list;
        }

        @Override
        public long lastModifiedTime(String location) throws IOException {
                File f = getValidatedFile(location, false);
                if (!f.exists()) {
                        throw new FileNotFoundException(f.getAbsolutePath());
                }
                return f.lastModified();
        }

        /**
         * won't functin properly
         *
         * @param location location location
         * @return result of invoking {@link LocalFileSource#lastModifiedTime(String)}
         */
        @Override
        @Deprecated
        public long creatingTime(String location) throws IOException {
                return lastModifiedTime(location);
        }

        @Override
        public long length(String location) throws IOException {
                File f = getValidatedFile(location, false);
                if (!f.exists()) {
                        throw new FileNotFoundException(f.getAbsolutePath());
                }
                return f.length();
        }

        @Override
        public InputStreamProvider provideStream(String location) throws IOException {
                final File file = getValidatedFile(location, false);
                return new InputStreamProvider() {
                        @Override
                        public InputStream get() {
                                try {
                                        return new FileInputStream(file);
                                } catch (FileNotFoundException e) {
                                        return null;
                                }
                        }
                };
        }

        @Override
        public void write(String location, InputStream inputStream) throws IOException {
                File file = getValidatedFile(location, false);
                if (file.exists() || file.createNewFile()) {
                        OutputStream outputStream = new FileOutputStream(file);
                        byte[] buf = new byte[1024];
                        int len;
                        while (-1 != (len = inputStream.read(buf))) {
                                outputStream.write(buf, 0, len);
                        }
                        outputStream.close();
                } else {
                        throw new FileNotFoundException();
                }
        }

        @Override
        public void move(String o, String n) throws IOException {
                File f = getValidatedFile(o, false);
                if (!f.renameTo(getValidatedFile(n, false))) {
                        throw new RuntimeException(new IOException());
                }
        }

        private void recursivelyDeleteDir(File dir) {
                File[] files = dir.listFiles();
                if (files != null) {
                        for (File f : files) {
                                if (f.isFile()) {
                                        if (!f.delete()) {
                                                throw new RuntimeException(new IOException());
                                        }
                                } else if (f.isDirectory()) {
                                        recursivelyDeleteDir(f);
                                }
                        }
                }
                if (!dir.delete()) {
                        throw new RuntimeException(new IOException());
                }
        }

        @Override
        public void delete(Collection<String> locations) throws IOException {
                for (String location : locations) {
                        File f = getValidatedFile(location, false);
                        if (f.exists())
                                recursivelyDeleteDir(f);
                }
        }
}
